//===--- CollectionAlgorithms.swift.gyb -----------------------*- swift -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

%{

# We know we will eventually get a Sequence.Element type.  Define
# a shorthand that we can use today.
IElement = "Iterator.Element"

}%

//===----------------------------------------------------------------------===//
// last
//===----------------------------------------------------------------------===//

extension Collection where Index : BidirectionalIndex {
  public var last: Iterator.Element? {
    return isEmpty ? nil : self[endIndex.predecessor()]
  }
}

//===----------------------------------------------------------------------===//
// index(of:)/index(where:)
//===----------------------------------------------------------------------===//

extension Collection where ${IElement} : Equatable {
  /// Returns the first index where `value` appears in `self` or `nil` if
  /// `value` is not found.
  ///
  /// - Complexity: O(`self.count`).
  @warn_unused_result
  public func index(of element: ${IElement}) -> Index? {
    if let result = _customIndexOfEquatableElement(element) {
      return result
    }

    for i in self.indices {
      if self[i] == element {
        return i
      }
    }
    return nil
  }
}

extension Collection {
  /// Returns the first index where `predicate` returns `true` for the
  /// corresponding value, or `nil` if such value is not found.
  ///
  /// - Complexity: O(`self.count`).
  @warn_unused_result
  public func index(
    @noescape where predicate: (${IElement}) throws -> Bool
  ) rethrows -> Index? {
    for i in self.indices {
      if try predicate(self[i]) {
        return i
      }
    }
    return nil
  }
}

//===----------------------------------------------------------------------===//
// indices
//===----------------------------------------------------------------------===//

extension Collection {
  /// Returns the range of valid index values.
  ///
  /// The result's `endIndex` is the same as that of `self`.  Because
  /// `Range` is half-open, iterating the values of the result produces
  /// all valid subscript arguments for `self`, omitting its `endIndex`.
  public var indices: Range<Index> {
    return Range(_start: startIndex, end: endIndex)
  }
}

//===----------------------------------------------------------------------===//
// MutableCollection
//===----------------------------------------------------------------------===//

//===----------------------------------------------------------------------===//
// partition()
//===----------------------------------------------------------------------===//

%{

partitionDocComment = """\
  /// Re-order the given `range` of elements in `self` and return
  /// a pivot index *p*.
  ///
  /// - Postcondition: For all *i* in `range.startIndex..<`\ *p*, and *j*
  ///   in *p*\ `..<range.endIndex`, `less(self[`\ *i*\ `],
  ///   self[`\ *j*\ `]) && !less(self[`\ *j*\ `], self[`\ *p*\ `])`.
  ///   Only returns `range.endIndex` when `self` is empty."""

orderingRequirementForPredicate = """\
  /// - Precondition: `isOrderedBefore` is a
  ///   [strict weak ordering](http://en.wikipedia.org/wiki/Strict_weak_order#Strict_weak_orderings)
  ///   over the elements in `self`."""

orderingRequirementForComparable = """\
  /// - Precondition: The less-than operator (`func <`) defined in
  ///   the `Comparable` conformance is a
  ///   [strict weak ordering](http://en.wikipedia.org/wiki/Strict_weak_order#Strict_weak_orderings)
  ///   over the elements in `self`."""

}%

% # Generate two versions: with explicit predicates and with
% # a Comparable requirement.
% for preds in [ True, False ]:

%   if preds:
extension MutableCollection where Index : RandomAccessIndex {

${partitionDocComment}
  ///
${orderingRequirementForPredicate}
  @warn_unused_result
  public mutating func partition(
    @noescape isOrderedBefore: (${IElement}, ${IElement}) -> Bool
  ) -> Index {

%   else:

extension MutableCollection
  where Index : RandomAccessIndex, ${IElement} : Comparable {

${partitionDocComment}
  ///
${orderingRequirementForComparable}
  @warn_unused_result
  public mutating func partition() -> Index {

%   end

    let maybeResult = _withUnsafeMutableBufferPointerIfSupported {
      (baseAddress, count) -> Index in
      var bufferPointer =
        UnsafeMutableBufferPointer(start: baseAddress, count: count)
      let unsafeBufferPivot = bufferPointer.partition(
%   if preds:
        isOrderedBefore: isOrderedBefore
%   end
        )
      return startIndex.advanced(
        by: numericCast(unsafeBufferPivot - bufferPointer.startIndex))
    }
    if let result = maybeResult {
      return result
    }

%   if preds:
    typealias EscapingBinaryPredicate =
      (${IElement}, ${IElement}) -> Bool
    var escapableIsOrderedBefore =
      unsafeBitCast(isOrderedBefore, to: EscapingBinaryPredicate.self)
    return _partition(
      &self, subRange: indices, isOrderedBefore: &escapableIsOrderedBefore)
%   else:
    return _partition(&self, subRange: indices)
%   end
  }
}

% end

//===----------------------------------------------------------------------===//
// sorted()
//===----------------------------------------------------------------------===//

%{

sortedDocCommentForPredicate = """\
  /// Returns an `Array` containing the sorted elements of `source`
  /// according to `isOrderedBefore`."""

sortedDocCommentForComparable = """\
  /// Returns an `Array` containing the sorted elements of `source`."""

sortDocCommentForPredicate = """\
  /// Sort `self` in-place according to `isOrderedBefore`."""

sortDocCommentForComparable = """\
  /// Sort `self` in-place."""

sortIsUnstableForPredicate = """\
  /// The sorting algorithm is not stable (can change the relative order of
  /// elements for which `isOrderedBefore` does not establish an order)."""

sortIsUnstableForComparable = """\
  /// The sorting algorithm is not stable (can change the relative order of
  /// elements that compare equal)."""

}%

% for Self in ['Sequence', 'MutableCollection']:

extension ${Self} where Self.Iterator.Element : Comparable {
${sortedDocCommentForComparable}
  ///
${sortIsUnstableForComparable}
  ///
${orderingRequirementForComparable}
  @warn_unused_result(${'mutable_variant: "sort"' if Self == 'MutableCollection' else ''})
  public func sorted() -> [Iterator.Element] {
    var result = ContiguousArray(self)
    result.sort()
    return Array(result)
  }
}

extension ${Self} {
${sortedDocCommentForPredicate}
  ///
${sortIsUnstableForPredicate}
  ///
${orderingRequirementForPredicate}
  @warn_unused_result(${'mutable_variant: "sort"' if Self == 'MutableCollection' else ''})
  public func sorted(
    @noescape isOrderedBefore:
      (${IElement}, ${IElement}) -> Bool
  ) -> [Iterator.Element] {
    var result = ContiguousArray(self)
    result.sort(isOrderedBefore: isOrderedBefore)
    return Array(result)
  }
}

% end

extension MutableCollection
  where
  Self.Index : RandomAccessIndex,
  Self.Iterator.Element : Comparable {

${sortDocCommentForComparable}
  ///
${sortIsUnstableForComparable}
  ///
${orderingRequirementForComparable}
  public mutating func sort() {
    let didSortUnsafeBuffer: Void? =
      _withUnsafeMutableBufferPointerIfSupported {
      (baseAddress, count) -> Void in
      var bufferPointer =
        UnsafeMutableBufferPointer(start: baseAddress, count: count)
      bufferPointer.sort()
      return ()
    }
    if didSortUnsafeBuffer == nil {
      _introSort(&self, subRange: self.indices)
    }
  }
}

extension MutableCollection where Self.Index : RandomAccessIndex {
${sortDocCommentForPredicate}
  ///
${sortIsUnstableForPredicate}
  ///
${orderingRequirementForPredicate}
  public mutating func sort(
    @noescape isOrderedBefore:
      (${IElement}, ${IElement}) -> Bool
  ) {
    typealias EscapingBinaryPredicate =
      (Iterator.Element, Iterator.Element) -> Bool
    let escapableIsOrderedBefore =
      unsafeBitCast(isOrderedBefore, to: EscapingBinaryPredicate.self)

    let didSortUnsafeBuffer: Void? =
      _withUnsafeMutableBufferPointerIfSupported {
      (baseAddress, count) -> Void in
      var bufferPointer =
        UnsafeMutableBufferPointer(start: baseAddress, count: count)
      bufferPointer.sort(isOrderedBefore: escapableIsOrderedBefore)
      return ()
    }
    if didSortUnsafeBuffer == nil {
      _introSort(
        &self,
        subRange: self.indices,
        isOrderedBefore: escapableIsOrderedBefore)
    }
  }
}

extension MutableCollection
  where Index : RandomAccessIndex {

  @available(*, unavailable, message: "slice the collection using the range, and call partition(isOrderedBefore:)")
  public mutating func partition(
    _ range: Range<Index>,
    isOrderedBefore: (${IElement}, ${IElement}) -> Bool
  ) -> Index {
    fatalError("unavailable function can't be called")
  }
}

extension MutableCollection
  where Index : RandomAccessIndex, ${IElement} : Comparable {

  @available(*, unavailable, message: "slice the collection using the range, and call partition()")
  public mutating func partition(_ range: Range<Index>) -> Index {
    fatalError("unavailable function can't be called")
  }
}

extension MutableCollection
  where
  Self.Index : RandomAccessIndex,
  Self.Iterator.Element : Comparable {

  @available(*, unavailable, renamed: "sort")
  public mutating func sortInPlace() {
    fatalError("unavailable function can't be called")
  }
}

extension MutableCollection where Self.Index : RandomAccessIndex {
  @available(*, unavailable, renamed: "sort(isOrderedBefore:)")
  public mutating func sortInPlace(
    @noescape _ isOrderedBefore: (Iterator.Element, Iterator.Element) -> Bool
  ) {
    fatalError("unavailable function can't be called")
  }
}

extension Collection where ${IElement} : Equatable {
  @available(*, unavailable, renamed: "index(of:)")
  @warn_unused_result
  public func indexOf(_ element: ${IElement}) -> Index? {
    fatalError("unavailable function can't be called")
  }
}

extension Collection {
  @available(*, unavailable, renamed: "index(where:)")
  @warn_unused_result
  public func indexOf(
    @noescape _ predicate: (${IElement}) throws -> Bool
  ) rethrows -> Index? {
    fatalError("unavailable function can't be called")
  }
}
